using System.Collections;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

namespace GameToolkit.Editor
{
    [CustomEditor(typeof(OverrideMaterialInstance)), CanEditMultipleObjects]
    public class OverrideMaterialInstanceEditor : UnityEditor.Editor
    {
        public static Renderer GetPrefabRenderer(Renderer targetRenderer)
        {
            if (targetRenderer == null)
                return null;
            Mesh mesh;
            if (targetRenderer is SkinnedMeshRenderer)
            {
                mesh = ((SkinnedMeshRenderer)targetRenderer).sharedMesh;
            }
            else if (targetRenderer is MeshRenderer)
            {
                var filter = targetRenderer.GetComponent<MeshFilter>();
                mesh = filter == null ? null : filter.sharedMesh;
            }
            else
            {
                mesh = null;
            }
            var asset = mesh == null ? null : AssetDatabase.GetAssetPath(mesh);
            var go = string.IsNullOrEmpty(asset) ? null : AssetDatabase.LoadAssetAtPath<GameObject>(asset);
            Renderer render = null;
            if (go != null)
            {
                var renderers = go.GetComponentsInChildren<Renderer>(true);
                for (int i = 0; render == null && i < renderers.Length; i++)
                {
                    var r = renderers[i];
                    if (r is SkinnedMeshRenderer && ((SkinnedMeshRenderer)r).sharedMesh == mesh)
                    {
                        render = r;
                    }
                    else if (r is MeshRenderer)
                    {
                        var f = r.GetComponent<MeshFilter>();
                        if (f != null && f.sharedMesh == mesh)
                            render = r;
                    }
                }
            }
            return render;
        }

        class MaterialEditing
        {
            public int index;
            public GUIContent title;
            public Material material;
            public bool isOverride;
        }

        SerializedProperty m_Materials;
        ReorderableList mMatLstGUI;
        ArrayList mMatLst;
        bool mDirty;

        private void OnEnable()
        {
            m_Materials = serializedObject.FindProperty("m_Materials");
            mMatLst = new ArrayList();
            mMatLstGUI = new ReorderableList(mMatLst, typeof(MaterialEditing));
            mMatLstGUI.draggable = false;
            mMatLstGUI.displayAdd = false;
            mMatLstGUI.displayRemove = false;
            mMatLstGUI.headerHeight = 20;
            mMatLstGUI.drawHeaderCallback = DrawMaterialHeaderGUI;
            mMatLstGUI.drawElementCallback = DrawMaterialElementGUI;

            if (!serializedObject.isEditingMultipleObjects)
            {
                mDirty = false;
                var renderer = ((OverrideMaterialInstance)target).GetComponent<Renderer>();
                var prefRenderer = GetPrefabRenderer(renderer);
                var mats = CacheUtil.GetListCahce<Material>();
                renderer.GetSharedMaterials(mats);
                var sharingMats = prefRenderer == null ? renderer.sharedMaterials : prefRenderer.sharedMaterials;
                bool apply = false;

                for (int i = 0; i < sharingMats.Length; i++)
                {
                    var mat = new MaterialEditing();
                    mat.index = i;
                    mat.material = sharingMats[i];
                    mat.title = new GUIContent(string.Format("#{0}:{1}", i, mat.material == null ? "-" : mat.material.name));
                    mat.isOverride = false;
                    mMatLst.Add(mat);
                }

                for (int i = 0; i < m_Materials.arraySize; i++)
                {
                    var element = m_Materials.GetArrayElementAtIndex(i);
                    var index = element.FindPropertyRelative("index").intValue;
                    if (index < sharingMats.Length)
                    {
                        var matProp = element.FindPropertyRelative("material");
                        var mat = matProp.objectReferenceValue as Material;
                        if (mat != null)
                        {
                            sharingMats[index] = mat;
                        }
                        else
                        {
                            if (index < mats.Count && mats[index] != null)
                                sharingMats[index] = mats[index];
                            matProp.objectReferenceValue = sharingMats[index];
                            apply = true;
                        }
                        var matsrc = (MaterialEditing)mMatLst[index];
                        matsrc.material = sharingMats[index];
                        matsrc.isOverride = true;
                    }
                }
                for (int i = 0; i < mMatLst.Count; i++)
                {
                    var matsrc = (MaterialEditing)mMatLst[i];
                    if (!matsrc.isOverride && i < mats.Count && mats[i] != null)
                    {
                        sharingMats[i] = mats[i];
                        matsrc.material = mats[i];
                    }
                }
                if (apply)
                    serializedObject.ApplyModifiedProperties();
            }
            else
            {
                mMatLst.Add(new GUIContent("Is Editing Multi-Objects Materials"));
            }
        }

        private void DrawMaterialElementGUI(Rect rect, int index, bool isActive, bool isFocused)
        {
            var obj = mMatLst[index];
            if (obj is MaterialEditing)
            {
                var editing = (MaterialEditing)obj;
                var width = EditorGUIUtility.labelWidth;
                EditorGUI.LabelField(new Rect(rect.x + 16, rect.y, width - 16, rect.height), editing.title);
                var pos = new Rect(rect.x + width, rect.y + 1, rect.width - width, rect.height - 2);
                EditorGUI.LabelField(pos, editing.title, "helpbox");
                var mat = EditorGUI.ObjectField(pos, editing.material, typeof(Material), false) as Material;
                if (mat != editing.material)
                {
                    editing.material = mat;
                    mDirty = true;
                }
                var willOverride = EditorGUI.Toggle(new Rect(rect.x, rect.center.y - 7.5f, 15f, 15f), editing.isOverride);
                mDirty |= willOverride != editing.isOverride;
                editing.isOverride = willOverride;
                //var position = EditorGUI.PrefixLabel(rect, editing.title);
            }
            else
            {
                EditorGUI.LabelField(rect, (GUIContent)obj);
            }
        }

        private void DrawMaterialHeaderGUI(Rect rect)
        {
            EditorGUI.LabelField(rect, "Override Materials");
            if (mMatLst.Count > 1 && mMatLst[0] is MaterialEditing)
            {
                var x = rect.xMax - 100;
                var y = rect.center.y - 8;
                var all = GUI.Button(new Rect(x, y, 50, 16), "All", "buttonleft");
                var none = GUI.Button(new Rect(x + 50, y, 50, 16), "None", "buttonright");
                if (all || none)
                {
                    for (int i = 0; i < mMatLst.Count; i++)
                    {
                        var mat = (MaterialEditing)mMatLst[i];
                        mDirty |= mat.isOverride != all;
                        mat.isOverride = all;
                    }
                }
            }
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
            serializedObject.Update();
            mMatLstGUI.DoLayoutList();
            if (mDirty)
            {
                mDirty = false;
                var overrids = CacheUtil.GetListCahce<MaterialEditing>();
                for (int i = 0; i < mMatLst.Count; i++)
                {
                    var mat = (MaterialEditing)mMatLst[i];
                    if (mat.isOverride)
                        overrids.Add(mat);
                }
                m_Materials.arraySize = overrids.Count;
                for (int i = 0; i < overrids.Count; i++)
                {
                    var ele = m_Materials.GetArrayElementAtIndex(i);
                    ele.FindPropertyRelative("index").intValue = overrids[i].index;
                    ele.FindPropertyRelative("material").objectReferenceValue = overrids[i].material;
                }
                serializedObject.ApplyModifiedProperties();
                var mom = (OverrideMaterialInstance)target;
                var renderer = mom.GetComponent<Renderer>();
                var mats = renderer.sharedMaterials;
                var newMats = new Material[mMatLst.Count];
                for (int i = 0; i < newMats.Length; i++)
                {
                    var mat = (MaterialEditing)mMatLst[i];
                    if (!mat.isOverride || !mom.IsFeatureOn)
                        newMats[i] = mat.material;
                    else if (mats.Length > i)
                        newMats[i] = mats[i];
                }
                renderer.sharedMaterials = newMats;
                EditorUtility.SetDirty(renderer);
            }
            if (!serializedObject.isEditingMultipleObjects)
            {
                EditorGUI.BeginDisabledGroup(true);
                var t = (OverrideMaterialInstance)target;
                EditorGUILayout.ObjectField("Material Overrider", t.Overrider, typeof(MaterialOverrider), false);
                EditorGUILayout.ObjectField("Material Group", t.group, typeof(MonoBehaviour), false);
                EditorGUI.EndDisabledGroup();
            }

            MaterialGroupOverriderEditor.DoCachedMaterialsGUI();
        }


    }
}
